#!/usr/bin/env bash
# FOSSology postinstall script
# SPDX-FileCopyrightText: © 2008-2013 Hewlett-Packard Development Company, L.P.
# SPDX-FileCopyrightText: © 2015 Siemens AG
#
# SPDX-License-Identifier: GPL-2.0-only
#
# This script is for things that need to be done on the runtime system
# after we've installed the bits on the filesystem. It needs to be
# idempotent, meaning that running it repeatedly will still do the
# right thing, regardless of the success of previous runs.

show_help() {
  cat <<EOF
Usage: fo-postinstall [options]
  -a or --agent           : agent specific actions
  -d or --database        : Create the FOSSology database
  -w or --web-only        : web interface specific actions
  -s or --scheduler-only  : scheduler specific actions
  -e or --everything      : all actions, default
  -o or --overwrite       : overwrite config files with new versions
  -l or --licenseref      : update the license_ref table with fossology supplied licenses
  -c or --common          : common actions
  -h or --help            : this help text
 Special options
  --no-running-database   : omit all database operations
  --force-decision        : Force update of SHA256 hash in decision tables
  --force-pfile           : Force calculation of SHA256 hash of pfile entries
  --force-encode          : Force recoding copyright and sister tables
  --debian                : running as part of debian installation step
  --python-experimental   : install experimental python dependencies
EOF
}

AGENT=''
DATABASE=''
WEBONLY=''
SCHEDULERONLY=''
EVERYTHING=true
OVERWRITE=''
LICENSEREF=''
COMMON=''
RUNNINGDATABASE=true
FORCEDECISION=''
FORCEPFILE=''
FORCEENCODE=''
PYTHONEXPERI=''
DEBIAN=''

## Options parsing and setup
# parse options
OPTS=$(getopt -o adwseolch --long agent,database,web-only,scheduler-only,everything,overwrite,licenseref,common,no-running-database,force-decision,force-pfile,force-encode,python-experimental,debian,help -n 'fo-postinstall' -- "$@")

if [[ $? -ne 0 ]]; then
   OPTS="--help"
fi

eval set -- "$OPTS"

# If any flags are sent, set them and make everything false
while true; do
   case "$1" in
      -a|--agent)            AGENT=true; EVERYTHING=''; shift;;
      -d|--database)         DATABASE=true; EVERYTHING=''; shift;;
      -w|--web-only)         WEBONLY=true; EVERYTHING=''; shift;;
      -s|--scheduler-only)   SCHEDULERONLY=true; EVERYTHING=''; shift;;
      -e|--everything)       EVERYTHING=true; shift;;
      -o|--overwrite)        OVERWRITE=true; shift;;
      -l|--licenseref)       LICENSEREF=true; EVERYTHING=''; shift;;
      -c|--common)           COMMON=true; EVERYTHING=''; shift;;
      --no-running-database) RUNNINGDATABASE=''; EVERYTHING=''; shift;;
      --force-decision)      FORCEDECISION='--force-decision'; shift;;
      --force-pfile)         FORCEPFILE='--force-pfile'; shift;;
      --force-encode)        FORCEENCODE='--force-encode'; shift;;
      --python-experimental) PYTHONEXPERI='--experimental'; shift;;
      --debian)              DEBIAN='--debian'; shift;;
      -h|--help)             show_help; exit;;
      --)                    shift; break;;
      *)                     echo "Error: option $1 not recognised"; exit 1;;
   esac
done

# If everything flags were sent, set everything to true to prevent overwrite
if [[ $OPTS == *" -e "* || $OPTS == *" --everything "* ]]; then
    EVERYTHING=true
fi

set -o errexit -o nounset -o pipefail

if [[ $EVERYTHING ]]; then
   echo "*** Running postinstall for everything ***"
   COMMON=true
   SCHEDULERONLY=true
   AGENT=true
   DATABASE=true
   WEBONLY=true
   LICENSEREF=true
fi

must_run_as_root() {
  # This must run as root.
  if [[ $(id -u) -ne 0 ]] ; then
    echo >&2 "ERROR: fo-postinstall must run as root."
    echo >&2 "Aborting."
    exit 1
  fi
}

read_config_value() {
   local line="$1"
   local key="$2"
   local out="$3"
   [[ "$out" =~ ^[a-zA-Z]+$ ]] || { echo >&2 "FATAL: bad parameters to read_config_value: $@"; exit 1; }
   if [[ $line =~ ^[[:space:]]*${key}[[:space:]]*\=[[:space:]]*([^[:space:]\']*) ]]; then
      eval "$out='${BASH_REMATCH[1]}'"
   fi
}

set_value_in_section() {
   echo "NOTE: writing $2=$3 in section $4 of $1"
   sed -e "s/\[$4\]/[$4]\n$2=$3/" -i.bak "$1"
}

REPO=''
PROJECTGROUP=''
CONFIG="@FO_SYSCONFDIR@/fossology.conf"
if [[ -f $CONFIG ]]; then
   while read line; do
      read_config_value "$line" path REPO
      read_config_value "$line" PROJECTGROUP PROJECTGROUP
   done < "$CONFIG"
   [[ $PROJECTGROUP =~ ^[a-z0-9]*$ ]] || { echo >&2 "ERROR: bad value for PROJECTGROUP in config"; exit 2; }
else
   echo >&2 "ERROR: Cannot find $CONFIG"
   exit 1
fi

if [[ $COMMON ]]; then
   must_run_as_root
   echo "*** Running postinstall for common actions***"

   # check path to repo
   [[ $REPO ]] || { echo >&2 "ERROR: path is not set in $CONFIG"; exit 1; }

   # set default when PROJECTGROUP is not set
   [[ $PROJECTGROUP ]] || {
      set_value_in_section "$CONFIG" PROJECTGROUP @FO_PROJECTGROUP@ 'DIRECTORIES'
      PROJECTGROUP="@FO_PROJECTGROUP@"
   }

   ## create user and group
   # Because we are doing these by name, in the multi-machine install case
   # we may end up with uid/gid being different across machines. This will
   # either need to be fixed by hand or with NFSv4 you can use rpc.idmapd
   # to do uid/gid mapping. More details will be provided in the multi-machine
   # documentation.

   # Make sure the user and group exist, if not then create
   echo "*** Creating user and group ***"

   # check for group
   if grep -q "^$PROJECTGROUP:" /etc/group; then
      echo "NOTE: group '$PROJECTGROUP' already exists, good."
   elif groupadd --system "$PROJECTGROUP"; then
      echo "NOTE: group '$PROJECTGROUP' created"
   else
      echo >&2 "ERROR: Unable to create group '$PROJECTGROUP'"
      exit 1
   fi

   # check for user
   if grep -q "^@FO_PROJECTUSER@:" /etc/passwd; then
      echo "NOTE: user '@FO_PROJECTUSER@' already exists, good."
      USERSHELL=$(grep "^@FO_PROJECTUSER@:" /etc/passwd | cut -d: -f 7)
      if [[ $USERSHELL == "/bin/false" ]]; then
         echo >&2 "ERROR: @FO_PROJECTUSER@ shell must be a real shell"
         exit 1
      fi
      USERHOME=$(grep "^@FO_PROJECTUSER@:" /etc/passwd | cut -d: -f 6)
      if [[ $USERHOME != "/home/@FO_PROJECTUSER@" ]]; then
         echo "NOTE: change user '@FO_PROJECTUSER@' homedir from $USERHOME to /home/@FO_PROJECTUSER@."
         mkdir --parents /home/@FO_PROJECTUSER@
         chown @FO_PROJECTUSER@:$PROJECTGROUP /home/@FO_PROJECTUSER@
         usermod --home /home/@FO_PROJECTUSER@ @FO_PROJECTUSER@
      fi
   elif useradd --comment "@FO_PROJECT@" --gid $PROJECTGROUP --create-home --shell /bin/bash --system @FO_PROJECTUSER@; then
      echo "NOTE: user '@FO_PROJECTUSER@' created"
      USERHOME="/home/@FO_PROJECTUSER@"
   else
      echo >&2 "ERROR: Unable to create user '@FO_PROJECTUSER@'"
      exit 1
   fi

   ## check for existance/ownership/permissions of needed directories
   echo "*** Making sure needed dirs exist with right ownership/permissions ***"

   if [[ -d "@FO_CACHEDIR@" ]]; then
       echo "*** clearing file cache ***"
       rm --force --recursive @FO_CACHEDIR@/*
   fi
   if [[ -d "$REPO" ]]; then
       echo "NOTE: Repository already exists at $REPO"
   fi

   mkdir --parents \
      "@FO_PROJECTSTATEDIR@" \
      "@FO_PROJECTSTATEDIR@/agents" \
      "@FO_CACHEDIR@" \
      "$REPO" \
      "@FO_REPODIR@/fossdash"

   chown root:$PROJECTGROUP "@FO_PROJECTSTATEDIR@" "@FO_PROJECTSTATEDIR@/agents" @FO_CACHEDIR@
   chmod 2775 "@FO_PROJECTSTATEDIR@" "@FO_PROJECTSTATEDIR@/agents" @FO_CACHEDIR@ "@FO_REPODIR@/fossdash"

   # make sure repo and its parent dir has the right permissions
   if [[ $(stat -c "%G" $REPO) != @FO_PROJECTGROUP@ ]]; then
     echo "NOTE: Repository group is not correct."
     echo "Fixing..This may take long duration, depending on your repository size."
     chown --recursive @FO_PROJECTUSER@:$PROJECTGROUP ${REPO%/*} $REPO
   fi
   chmod 2770 ${REPO%/*} $REPO

   # make sure group can traverse user home dir
   chmod 750 "$USERHOME"

   # make install sets Db.conf's mode to 660, but can't chgrp it because
   # the group doesn't exist until we create it above. So chgrp now
   chgrp $PROJECTGROUP @FO_SYSCONFDIR@/Db.conf || echo >&2 "ERROR: failed to chgrp @FO_SYSCONFDIR@/Db.conf"

   cat <<EOF
NOTE: Running the PostgreSQL vacuum and analyze command can result in a large database performance improvement.
      We suggest that you either configure postgres to run its autovacuum and autoanalyze daemons, or maintagent -D in a cron job, or run Admin > Maintenance on a regular basis.
      Admin > Dashboard will show you the last time vacuum and analyze have been run.
EOF
fi

[[ $PROJECTGROUP ]] || { echo "PROJECTGROUP is not set"; exit 2; }

if [[ $WEBONLY ]]; then
   echo "*** Running postinstall for web-only actions***"
   LICENSEREF=true
fi

########################################################################

if [[ $DATABASE && $RUNNINGDATABASE ]]; then
   @FO_LIBEXECDIR@/dbcreate
fi # end of DATABASE

if [[ $LICENSEREF && $RUNNINGDATABASE ]]; then
    must_run_as_root
    echo "*** update the database and license_ref table ***"
    @FO_LIBEXECDIR@/fossinit.php -l -c @FO_SYSCONFDIR@ $FORCEDECISION $FORCEPFILE $FORCEENCODE
fi # end of license reference

########################################################################

if [[ $AGENT ]]; then
   must_run_as_root
   # change all files ownership to @FO_PROJECTUSER@:$PROJECTGROUP
   chown --recursive @FO_PROJECTUSER@:$PROJECTGROUP @FO_MODDIR@ @FO_LIBEXECDIR@ @FO_SYSCONFDIR@
   chown --silent @FO_PROJECTUSER@:$PROJECTGROUP @FO_INCLUDEDIR@/libfossology.h
   chown --silent @FO_PROJECTUSER@:$PROJECTGROUP @FO_MAN1DIR@/cp2foss.1{,.gz} @FO_MAN1DIR@/fossjobs.1{,.gz} || echo
   # Install Python dependencies for source install only
   if [[ "@FO_DESTDIR@" != *"debian"* ]]; then
     @FO_LIBEXECDIR@/fo-install-pythondeps -y --runtime --user @FO_PROJECTUSER@ --experimental $DEBIAN
   fi
fi

if [[ $AGENT && $RUNNINGDATABASE ]]; then
   if ! @FO_LIBEXECDIR@/fo_dbcheck.php -c @FO_SYSCONFDIR@; then
     echo "FATAL: unable to connect to database, please check @FO_SYSCONFDIR@/Db.conf"
     exit 1
   fi
   echo "Database connectivity is good."
fi # end of AGENT

########################################################################

if [[ $SCHEDULERONLY ]]; then
   must_run_as_root
   echo "*** Setting up scheduler ***"

   # Create the scheduler log directory.
   if [[ -f @FO_LOGDIR@ ]] ; then
     # Must be a directory and not a file
     rm --force @FO_LOGDIR@
   fi
   mkdir --parents @FO_LOGDIR@
   chown --recursive @FO_PROJECTUSER@:$PROJECTGROUP @FO_LOGDIR@
   chmod 2775 @FO_LOGDIR@
   find @FO_LOGDIR@ -type f -print0 | xargs --no-run-if-empty --null chmod 660
fi # end of SCHEDULERONLY

########################################################################
if [[ $WEBONLY ]]; then
  must_run_as_root
  RESTART_MESSAGE=false

  echo "*** Setting up the web interface ***"

  # See if web server user exists, if so add to the group.
  # check for www-data (Debian, etc)
  if grep -q "^www-data:" /etc/passwd; then
    echo "NOTE: Adding user www-data to group $PROJECTGROUP"
    # this is smart enough to not add multiple times so it's ok to repeat
    usermod --groups $PROJECTGROUP --append www-data
  fi

  # check for apache (RHEL/CentOS, etc)
  if grep -q "^apache:" /etc/passwd; then
    echo "NOTE: Adding user apache to group $PROJECTGROUP"
    # this is smart enough to not add multiple times so it's ok to repeat
    usermod --groups $PROJECTGROUP --append apache
  fi

  # Enable mod_rewrite for REST API
  if [[ -f /etc/httpd/conf.modules.d/00-base.conf ]]; then
    # We are on RHEL
    if ! egrep -q "^LoadModule rewrite_module modules/mod_rewrite.so" /etc/httpd/conf.modules.d/00-base.conf; then
      echo "NOTE: Enabling apache module rewrite"
      echo "LoadModule rewrite_module modules/mod_rewrite.so" >> /etc/httpd/conf.modules.d/00-base.conf
      RESTART_MESSAGE=true
    fi
  else
    # We are on Debian
    if ! a2query -q -m rewrite; then
      echo "NOTE: Enabling apache module rewrite"
      /usr/sbin/a2enmod rewrite
      RESTART_MESSAGE=true
    fi
  fi

  # Enable fossology for source install
  if [[ "@FO_DESTDIR@" != *"debian"* ]]; then
    if [[ -d @FO_APACHE2SITE_DIR@ ]]; then
      if [[ -f @FO_APACHE2SITE_DIR@/fossology.conf ]]; then
        echo "Fossology already enabled for apache2";
      else
        echo "NOTE: enabling fossology site for apache2";
        ln -s @FO_DESTDIR@@FO_SYSCONFDIR@/conf/src-install-apache-example.conf @FO_APACHE2SITE_DIR@/fossology.conf;
        @FO_APACHE2_EN_SITE@ fossology.conf;
        # Exit code 1, soft link points to wrong file. Fix it.
        if [[ $? -eq 1 ]]; then
          @FO_APACHE2_DIS_SITE@ fossology.conf;
          @FO_APACHE2_EN_SITE@ fossology.conf;
        fi;
        RESTART_MESSAGE=true
      fi;
    elif [[ -d @FO_HTTPD_SITE_DIR@ ]]; then
      if [[ -f @FO_HTTPD_SITE_DIR@/fossology.conf ]]; then
        echo "Fossology already enabled for httpd";
      else
        echo "NOTE: enabling fossology site for httpd";
        ln -s @FO_DESTDIR@@FO_SYSCONFDIR@/conf/src-install-apache-example.conf @FO_HTTPD_SITE_DIR@/fossology.conf;
        RESTART_MESSAGE=true
      fi;
    fi;
  fi;

  # apache2 reload is only necessary when it is running.
  if /etc/init.d/apache2 status > /dev/null; then
    /etc/init.d/apache2 reload
    if [[ "$RESTART_MESSAGE" = true ]]; then
      echo "NOTE: Restart apache to apply the changes"
    fi
  fi
fi # end of WEBONLY

########################################################################

echo "FOSSology postinstall complete, but sure to complete the remaining"
echo "  steps in the INSTALL instructions."
